﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog.Core;
using SqlSugar;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WLYT.Application.Interfaces;
using WLYT.Common;
using WLYT.Core;
using WLYT.Domain.Entries;
using WLYT.Domain.Enum;
using WLYT.Domain.Model;
using WLYT.Domain.Model.CAEAModel;

namespace WLYT.Application.Services
{
    public class OrderService(IRepository<OrderItem> orderItemRepository,
                              IRepository<InOrderItem> inOrderItemRepository,
                              IRepository<Order> orderRepository,
                              IRepository<WarehouseSend> warehouseSendRepository,
                              IInventoryService inventoryService,
                              IRepository<Sys_Warehouse> sysWarehouseRepository,
                              IConfiguration configuration,
                              IWarehouseService warehouseService,
                              ICAEAService caeaService,
                              ISqlSugarClient db,
                              ILogger<OrderService> logger) : IOrderService
    {
        private List<string> BoxExportList = new List<string>() { "1", "2" };
        /// <summary>
        /// 入库完成
        /// </summary>
        /// <param name="reelId"></param>
        /// <returns></returns>
        public async Task<ApiResult> InStock(SMTModel model)
        {
            ApiResult result = new ApiResult();
            await inOrderItemRepository.InsertAsync(new InOrderItem
            {
                ReelId = model.ReelId,
                LocationNo = model.LocationNo,
                WarehouseNo = model.WarehouseNo,
                OriginalCode = model.OriginalCode,
                InOrOutTime = DateTime.Now
            });
            await inventoryService.InStock(model);
            return result;
        }

        private async Task<string> GenerateNumber()
        {
            int sequenceNumber = 1;
            string orderNo = await orderRepository.AsQueryable().Where(p => p.Type == 0 && p.CreateTime > DateTime.Today).OrderByDescending(p => p.Id).Select(p => p.OrderNo).FirstAsync();
            if (!string.IsNullOrEmpty(orderNo))
            {
                sequenceNumber = int.Parse(orderNo.Substring(orderNo.Length - 4)) + 1;
            }
            return $"WLYT{DateTime.Now:yyyyMMdd}{sequenceNumber.ToString().PadLeft(4, '0')}";
        }

        /// <summary>
        /// 同步订单
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<ApiResult> SyncOrder(OrderModel model)
        {
            ApiResult result = new();
            try
            {
                if (string.IsNullOrEmpty(model.OrderNo))
                {
                    model.OrderNo = await GenerateNumber();
                }
                if (model.ReelList.Count == 0)
                {
                    result.Code = 1;
                    result.Message = $"单据号【{model.OrderNo}】物料盘数需大于0";
                    return result;
                }
                List<Inventory> inventoryList = await inventoryService.GetListAsync(model.ReelList.Select(q => q.ReelId).ToList());
                await db.Ado.BeginTranAsync();
                Order order = await orderRepository.InsertReturnEntityAsync(new Order
                {
                    CreateTime = DateTime.Now,
                    CompleteTime = null,
                    OutTime = null,
                    OrderNo = model.OrderNo,
                    Status = 0,
                });
                List<OrderItem> reelList = new List<OrderItem>();
                foreach (var item in model.ReelList)
                {
                    Inventory detail = inventoryList.FirstOrDefault(p => p.ReelId == item.ReelId);
                    if (detail == null)
                    {
                        throw new Exception($"物料【{item.ReelId}】库存信息不是在库状态");
                    }
                    reelList.Add(new OrderItem
                    {
                        CreateTime = DateTime.Now,
                        InOrOutTime = null,
                        BoxCode = "",
                        Export = "",
                        IsUpLoad = false,
                        Status = (int)OutOrderStatus.待出库,
                        Qrcode = detail.Qrcode,
                        OrderId = order.Id,
                        OrderNo = order.OrderNo,
                        Type = order.Type,
                        ReelId = detail.ReelId,
                        WarehouseNo = detail.WarehouseNo,
                        LocationNo = detail.LocationNo,
                        OriginalCode = detail.OriginalCode,
                        LotNo = detail.LotNo,
                        Msl = detail.Msl,
                        Qty = detail.Qty,
                        Size = detail.Size,
                        Diameter = detail.Diameter,
                        ExpirationTime = detail.ExpirationTime,
                        GRTime = detail.GRTime,
                        SupplierId = detail.SupplierId,
                        SupplierName = detail.SupplierName,
                        Thickness = detail.Thickness,
                    });
                }
                await orderItemRepository.InsertRangeAsync(reelList);
                await inventoryService.UpdateAsync(reelList.Select(q => q.ReelId).ToList());
                await db.Ado.CommitTranAsync();
            }
            catch (Exception ex)
            {
                await db.Ado.RollbackTranAsync();
                result.Code = 1;
                result.Message = ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 出库完成
        /// </summary>
        /// <param name="reelId"></param>
        /// <returns></returns>
        public async Task<ApiResult> OutStock(string reelId)
        {
            ApiResult result = new ApiResult();
            OrderItem item = await orderItemRepository.GetFirstAsync(p => p.ReelId == reelId && (p.Status == (int)OutOrderStatus.出库完成 || p.Status == (int)OutOrderStatus.出库中));
            if (item == null)
            {
                result.Code = 1;
                result.Message = $"物料信息不存在";
                return result;
            }
            if (item.Status == (int)OutOrderStatus.出库中)
            {
                await inventoryService.OutStock(item.ReelId, item.OrderNo);
                await orderItemRepository.UpdateAsync(p => new OrderItem { Status = (int)OutOrderStatus.出库完成, InOrOutTime = DateTime.Now }, p => p.Id == item.Id);
                await warehouseSendRepository.DeleteAsync(p => p.ReelId == item.ReelId);
            }
            result.Message = $"物料【{item.ReelId}】出库完成";
            result.Data = item;
            return result;
        }

        /// <summary>
        /// 出库NG
        /// </summary>
        /// <param name="reelId"></param>
        /// <returns></returns>
        public async Task<ApiResult> OutStockNG(string reelId)
        {
            ApiResult result = new ApiResult();
            var item = await orderItemRepository.GetFirstAsync(p => p.ReelId == reelId && (p.Status == (int)OutOrderStatus.出库中 || p.Status == (int)OutOrderStatus.出库完成 || p.Status == (int)OutOrderStatus.出库失败));
            if (item == null)
            {
                result.Code = 1;
                result.Message = $"物料【{reelId}】没有出库任务";
                return result;
            }
            if (item.Status == (int)OutOrderStatus.出库完成 || item.Status == (int)OutOrderStatus.出库失败)
            {
                result.Message = $"物料【{reelId}】已出库，请勿重复出库";
                return result;
            }
            //同步上游WMS
            ApiResult apiResult = await caeaService.OutStockUpload(item.OrderNo, item.ReelId, 1);
            if (apiResult.Code != 0)
            {
                return apiResult;
            }
            await inventoryService.OutStock(item.ReelId, item.OrderNo, InventoryType.出库NG);
            await orderItemRepository.UpdateAsync(p => new OrderItem { Status = (int)OutOrderStatus.出库失败, InOrOutTime = DateTime.Now, IsUpLoad = true }, p => p.Id == item.Id);
            await warehouseSendRepository.DeleteAsync(p => p.ReelId == item.ReelId);
            return result;
        }

        /// <summary>
        /// 装箱完成
        /// </summary>
        /// <param name="reelId"></param>
        /// <param name="boxCode"></param>
        /// <returns></returns>
        public async Task<ApiResult> PickCompleted(string reelId, string boxCode = "")
        {
            ApiResult result = new ApiResult();
            var item = await orderItemRepository.AsQueryable().Where(p => p.ReelId == reelId).OrderByDescending(p => p.Id).Select(p => new
            {
                p.Id,
                p.Status,
                p.Export,
                p.OrderNo,
            }).FirstAsync();
            if (item == null)
            {
                result.Code = 1;
                result.Message = $"找不到物料【{reelId}】";
                return result;
            }
            if (item.Status > (int)OutOrderStatus.出库完成)
            {
                result.Message = $"物料【{reelId}】已装箱";
                return result;
            }
            if (item.Status < (int)OutOrderStatus.出库完成)
            {
                result.Code = 1;
                result.Message = $"物料【{reelId}】未出库";
                return result;
            }
            await orderItemRepository.UpdateAsync(p => new OrderItem { Status = (int)OutOrderStatus.装箱完成, PickTime = DateTime.Now, BoxCode = boxCode }, p => p.Id == item.Id);
            result.Message = $"物料【{reelId}】装箱完成，出料口【{item.Export}】";
            if (!item.OrderNo.StartsWith("WLYT"))
            {
                //通知MES，装箱完成
                await caeaService.PickCompleted(item.OrderNo, reelId, boxCode);
            }
            if (await OrderIsCompleted(item.OrderNo))
            {
                result.Code = 2;//订单装完
                result.Message = $"物料【{reelId}】装箱完成，订单装箱完成【{item.OrderNo}】，出料口【{item.Export}】";
            }
            return result;
        }

        /// <summary>
        /// 订单是否全部装箱完成
        /// </summary>
        /// <param name="orderNo"></param>
        /// <returns></returns>
        public async Task<bool> OrderIsCompleted(string orderNo)
        {
            if (!await orderItemRepository.IsAnyAsync(p => p.OrderNo == orderNo && (p.Status == (int)OutOrderStatus.出库中 || p.Status == (int)OutOrderStatus.待出库)))
            {
                await orderItemRepository.UpdateAsync(p => new OrderItem { Status = (int)OutOrderStatus.任务完成 }, p => p.OrderNo == orderNo);
                await orderRepository.UpdateAsync(p => new Order { Status = (int)OrderStatus.任务完成, TransportTime = DateTime.Now }, p => p.OrderNo == orderNo);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 到达末端
        /// </summary>
        /// <param name="boxCode"></param>
        /// <returns></returns>
        public async Task<ApiResult> BoxArriveEnd(string boxCode)
        {
            ApiResult result = new ApiResult();
            string orderNo = await orderItemRepository.AsQueryable().Where(p => p.BoxCode == boxCode && p.Status == (int)OutOrderStatus.任务完成).OrderByDescending(p => p.Id).Select(p => p.OrderNo).FirstAsync();
            if (string.IsNullOrEmpty(orderNo))
            {
                result.Code = 1;
                result.Message = $"箱子【{boxCode}】没有该数据";
                return result;
            }
            if (!orderNo.StartsWith("WLYT"))
            {
                //通知MES，到达末端
                await caeaService.BoxArriveEnd(orderNo, boxCode);
            }
            return result;
        }

        /// <summary>
        /// 入库定时上传
        /// </summary>
        /// <returns></returns>
        public async Task InStockUpload()
        {
            DateTime time = DateTime.Now.AddHours(-1);
            var inList = await inOrderItemRepository.AsQueryable()
                .Where(p => p.IsUpLoad == false && p.InOrOutTime > time)
                .Select(p => new
                {
                    p.Id,
                    p.ReelId,
                    p.WarehouseNo,
                    p.LocationNo
                })
                .ToListAsync();
            if (inList.Count > 0)
            {
                List<long> idList = new List<long>();
                foreach (var item in inList)
                {
                    ApiResult apiResult = await caeaService.InStockUpload(item.ReelId, item.WarehouseNo, item.LocationNo);
                    if (apiResult.Code == 0)
                    {
                        idList.Add(item.Id);
                    }
                }
                if (idList.Count > 0)
                {
                    await inOrderItemRepository.UpdateAsync(p => new InOrderItem { IsUpLoad = true }, p => idList.Contains(p.Id));
                }
            }
        }

        /// <summary>
        /// 出库定时上传
        /// </summary>
        /// <returns></returns>
        public async Task OutStockUpload()
        {
            DateTime time = DateTime.Now.AddHours(-1);
            var outList = await orderItemRepository.AsQueryable()
                .Where(p => p.IsUpLoad == false && p.Status >= (int)OutOrderStatus.出库完成 && p.InOrOutTime > time)
                .Select(p => new
                {
                    p.Id,
                    p.OrderNo,
                    p.ReelId,
                })
                .ToListAsync();
            if (outList.Count > 0)
            {
                List<long> idList = new List<long>();
                foreach (var item in outList)
                {
                    ApiResult apiResult = await caeaService.OutStockUpload(item.OrderNo, item.ReelId);
                    if (apiResult.Code == 0)
                    {
                        idList.Add(item.Id);
                    }
                }
                if (idList.Count > 0)
                {
                    await orderItemRepository.UpdateAsync(p => new OrderItem { IsUpLoad = true }, p => idList.Contains(p.Id));
                }
            }
        }

        /// <summary>
        /// 生成出库任务
        /// </summary>
        /// <returns></returns>
        public async Task CreateWarehouseTask()
        {
            int size = 30;
            int minSize = 1;
            foreach (var map in BoxExportList)
            {
                if (!await orderRepository.IsAnyAsync(p => p.Export == map && (p.Status == (int)OrderStatus.出库中 || p.Status == (int)OrderStatus.部分运输开始)))
                {
                    var orderNo = await orderRepository.AsQueryable().Where(p => p.Status == (int)OrderStatus.待出库).Select(p => p.OrderNo).FirstAsync();
                    if (!string.IsNullOrEmpty(orderNo))
                    {
                        List<WarehouseSend> warehouseSends = new List<WarehouseSend>();
                        List<OrderItem> orderItems = await orderItemRepository.GetListAsync(p => p.OrderNo == orderNo && p.Status >= 0);

                        var originalCodeList = orderItems.GroupBy(p => p.OriginalCode).Select(p => new
                        {
                            OriginalCode = p.Key,
                            Count = p.Count(),
                        }).OrderByDescending(p => p.Count).ToList();
                        int no = 1;
                        int position = 0;
                        foreach (var i in originalCodeList)
                        {
                            if (i.Count <= minSize) continue;
                            var list = orderItems.Where(p => p.OriginalCode == i.OriginalCode).ToList();
                            for (int num = 0; num <= list.Count / size; num++)
                            {
                                var newList = list.Skip(size * (num - 1)).Take(size);
                                warehouseSends.AddRange(newList.Select(item => new WarehouseSend
                                {
                                    OrderNo = item.OrderNo + "_" + no,
                                    WarehouseNo = item.WarehouseNo,
                                    OriginalCode = item.OriginalCode,
                                    ReelId = item.ReelId,
                                    Export = item.Export,
                                    LocationNo = item.LocationNo,
                                    Position = position
                                }));
                                position++;
                                if (position > 5)
                                {
                                    no++;
                                    position = 0;
                                }
                            }
                        }

                        var lastOriginalCode = originalCodeList.Where(p => p.Count <= minSize).Select(p => p.OriginalCode).ToList();
                        var lastOrderItem = orderItems.Where(p => lastOriginalCode.Contains(p.OriginalCode)).ToList();
                        for (int num = 0; num <= lastOrderItem.Count / size; num++)
                        {
                            var newList = lastOrderItem.Skip(size * (num - 1)).Take(size);
                            warehouseSends.AddRange(newList.Select(item => new WarehouseSend
                            {
                                OrderNo = item.OrderNo + "_" + no,
                                WarehouseNo = item.WarehouseNo,
                                OriginalCode = item.OriginalCode,
                                ReelId = item.ReelId,
                                Export = item.Export,
                                LocationNo = item.LocationNo,
                                Position = position
                            }));
                            position++;
                            if (position > 5)
                            {
                                no++;
                                position = 0;
                            }
                        }

                        await orderRepository.UpdateAsync(p => new Order { Status = (int)OrderStatus.出库中, OutTime = DateTime.Now, Export = map }, p => p.OrderNo == orderNo);
                        await orderItemRepository.UpdateAsync(p => new OrderItem { Status = (int)OutOrderStatus.出库中, Export = map }, p => p.OrderNo == orderNo && p.Status >= 0);
                        await warehouseSendRepository.InsertRangeAsync(warehouseSends);
                    }
                }
            }
        }

        /// <summary>
        /// 下发任务
        /// </summary>
        /// <returns></returns>
        public async Task SendTask()
        {
            List<Sys_Warehouse> warehouseList = await sysWarehouseRepository.GetListAsync(true);
            if(await warehouseSendRepository.IsAnyAsync(p => p.IsSend == true)) return;
            List<WarehouseSend> allSendList = await warehouseSendRepository.GetListAsync();
            string orderNo = allSendList.OrderBy(p => p.CreateTime).Select(p => p.OrderNo).FirstOrDefault();
            foreach (Sys_Warehouse warehouse in warehouseList)
            {
                List<WarehouseSend> sendList = allSendList.Where(p => p.IsSend == false && p.WarehouseNo == warehouse.Name && p.OrderNo == orderNo).ToList();
                if (sendList.Count == 0) continue;
                SMTModel model = new SMTModel
                {
                    WarehouseNo = warehouse.Name,
                    DeliveryNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
                    IP = warehouse.IP,
                    ReelIds = sendList.Select(p => p.ReelId).ToList(),
                };
                ApiResult result = await warehouseService.SendTask(model);
                if (result.Code == 0 || result.Code == 2)
                {
                    await warehouseSendRepository.UpdateAsync(p => new WarehouseSend { IsSend = true, SendTime = DateTime.Now }, p => model.ReelIds.Contains(p.ReelId));
                    if (result.Code == 2)
                    {
                        List<string> errReels = result.Data.ConvertObject<List<string>>();
                        logger.LogError($"下发任务【{warehouse.Name}】，错误信息：{result.Message}");
                        foreach (string item in errReels)
                        {
                            await OutStockNG(item);
                        }
                    }
                }
                else
                {
                    logger.LogError($"下发任务【{warehouse.Name}】，错误信息：{result.Message}");
                }
            }

        }
    }
}
